package cn.dengta.webapp.user.entity;

import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import javax.validation.constraints.NotNull;

import cn.dengta.common.model.*;
import cn.dengta.common.util.Common;
import cn.dengta.common.web.Client;
import cn.dengta.context.model.LabelI18nProxy;
import cn.dengta.context.model.UserEnumI18nKey;
import cn.dengta.webapp.base.entity.BizEntity;
import cn.dengta.webapp.user.model.Gender;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import me.codeplayer.util.*;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

/**
 * 第三方授权登录实体
 *
 * @author lgy
 * @since 2022-02-24
 */
@Getter
@Setter
@Accessors(chain = true)
public class OauthUser extends BizEntity {

	public static final String OAUTH_CALLBACK_SRC = "oauthCallbackSrc";
	/** 认证用户code标识 key */
	public static final String OAUTH_USER_KEY = "oauthUser";
	/** 邀请码参数 */
	public static final String INVITE_CODE_PARAM = "utm_term";
	/** 邀请码 session key */
	public static final String INVITE_CODE_KEY = "inviteCode";

	/** 用户id */
	private Long userId;
	/** 昵称 */
	private String nickname;
	/** 第三方类型 */
	private String thirdParty;
	/** 第三方id */
	private String thirdPartyId;
	/** 头像 */
	private String headImgUrl;
	/** 绑定时间 */
	private Date bindTime;
	/** 用户信息 */
	private String userInfo;
	/** 备注 */
	private String remark;
	/** token */
	private String accessToken;
	/** token有效期截止时间 */
	private Date tokenExpireTime;

	private transient String username;
	private transient Gender gender;
	private transient Date birthday;
	protected transient Client client;

	public static OauthUser of(Long id) {
		return new OauthUser().withId(id);
	}

	public OauthUser init(String headImgUrl, String nickname, String thirdParty, String thirdPartyId, String userInfo, String accessToken, Date tokenExpireTime) {
		this.headImgUrl = X.expectNotNull(headImgUrl, "");
		this.userInfo = X.expectNotNull(userInfo, "");
		this.nickname = StringUtil.limitChars(nickname, 40, null);
		this.thirdParty = thirdParty;
		this.thirdPartyId = thirdPartyId;
		this.accessToken = accessToken;
		this.tokenExpireTime = tokenExpireTime;
		this.remark = "";
		return this;
	}

	public OauthUser init(OauthUser input) {
		this.headImgUrl = X.expectNotNull(input.headImgUrl, headImgUrl);
		this.nickname = input.nickname == null ? nickname : StringUtil.limitChars(input.nickname, 40, null);
		this.userInfo = X.expectNotNull(input.userInfo, userInfo);
		this.username = X.expectNotNull(input.username, username);
		return this;
	}

	public OauthUser syncValue() {
		this.username = this.nickname;
		this.gender = null;
		this.birthday = null;
		return this;
	}

	public OauthUser setOpenId(Client client, String openId) {
		this.client = client;
		int index = getOpenIdIndex(client);
		if (index >= 0) {
			String[] parts = getOpenIds(true);
			parts[index] = StringUtil.toString(openId);
			this.remark = ArrayUtil.join(parts, Common.SEP);
		}
		return this;
	}

	@Nullable
	public String getWechatOpenId(Client client) {
		int index = getOpenIdIndex(client);
		if (index >= 0) {
			return ArrayUtils.get(getOpenIds(false), index);
		}
		return null;
	}

	public String[] getOpenIds(boolean create) {
		String[] parts = null;
		if (StringUtil.notEmpty(remark)) {
			parts = StringUtils.splitByWholeSeparatorPreserveAllTokens(remark, Common.SEP);
		}
		if (create && X.size(parts) != 2) {
			parts = new String[] { "", "" };
		}
		return parts;
	}

	protected int getOpenIdIndex(Client client) {
		if (client != null && ThirdOauthType.WECHAT.alias.equals(thirdParty)) {
			switch (client) {
				case WECHAT_MP:
					return 0;
				case WECHAT:
				case WAP:
				case APP_ANDROID:
				case APP_IOS:
					return 1;
				default:
			}
		}
		return -1;
	}

	@Override
	public void setId(Long id) {
		this.id = id;
	}

	public boolean isBind() {
		return OauthStatus.BIND.eq(status);
	}

	public boolean isUnBind() {
		return OauthStatus.UNBIND.eq(status);
	}

	public boolean canUse() {
		return status >= OauthStatus.WAIT_BIND.value;
	}

	public boolean isWaitBind() {
		return OauthStatus.WAIT_BIND.eq(status);
	}

	public boolean canSkip() {
		return ThirdOauthType.FAST_MOBILE.alias.equals(thirdParty);
	}

	/**
	 * 授权状态
	 */
	public enum OauthStatus implements LabelI18nProxy<OauthStatus, Integer> {
		UNBIND(-1, UserEnumI18nKey.OAUTH_STATUS_UNBIND),
		WAIT_BIND(0, UserEnumI18nKey.OAUTH_STATUS_WAIT_BIND),
		BIND(1, UserEnumI18nKey.OAUTH_STATUS_BIND);

		public final Integer value;

		final ValueProxy<OauthStatus, Integer> proxy;

		OauthStatus(Integer value, String label) {
			this.value = value;
			this.proxy = new ValueProxy<>(this, value, label);
		}

		public static OauthStatus[] getValues() {
			return ValueProxy.getCachedArray(OauthStatus.class, OauthStatus::values);
		}

		public static OauthStatus of(Integer value) {
			return WAIT_BIND.getValueOf(value);
		}

		@Override
		public ValueProxy<OauthStatus, Integer> getProxy() {
			return proxy;
		}
	}

	/**
	 * 第三方授权类型
	 */
	@Getter
	public enum ThirdOauthType implements ValueProxyImpl<ThirdOauthType, Integer> {
		/** TikTok */
		TIK_TOK("tiktok", "TikTok", true, 1, ThirdOauthType.BIZ_WAP | ThirdOauthType.BIZ_ANDROID | ThirdOauthType.BIZ_IOS),
		/** FaceBook */
		FACEBOOK("facebook", "FaceBook", true, 1, ThirdOauthType.BIZ_WAP | ThirdOauthType.BIZ_ANDROID | ThirdOauthType.BIZ_IOS),
		/** Google */
		GOOGLE("google", "Google", true, 1, ThirdOauthType.BIZ_ANDROID),
		/** Apple */
		APPLE("apple", "Apple", true, 1, ThirdOauthType.BIZ_IOS),
		/** wechat */
		WECHAT("wechat", "Wechat", true, 0, ThirdOauthType.BIZ_WAP | ThirdOauthType.BIZ_ANDROID),
		/** 中国移动 */
		FAST_MOBILE("fastmobile", "中国移动", true, 0, ThirdOauthType.BIZ_ANDROID),
		//
		;
		/** 标识：wap端 */
		public static final int BIZ_WAP = 1;
		/** 标识：Android端 */
		public static final int BIZ_ANDROID = 1 << 1;
		/** 标识：iOS端 */
		public static final int BIZ_IOS = 1 << 2;

		public final String alias;
		public final int bizFlag;
		public boolean enable;
		/** 范围：0=国内；1=海外；2=全部 */
		public Integer scope;
		final ValueProxy<ThirdOauthType, Integer> proxy;

		ThirdOauthType(String alias, String label, boolean enable, Integer scope, int bizFlag) {
			this.alias = alias;
			this.bizFlag = bizFlag;
			this.enable = enable;
			this.scope = scope;
			this.proxy = new ValueProxy<>(this, ordinal(), label);
		}

		ThirdOauthType(String alias, String label, boolean enable, Integer scope) {
			this(alias, label, enable, scope, Status.NO.value);
		}

		@Override
		public ValueProxy<ThirdOauthType, Integer> getProxy() {
			return proxy;
		}

		public static ThirdOauthType[] getValues() {
			return ValueProxy.getCachedArray(ThirdOauthType.class, ThirdOauthType::values);
		}

		public static ThirdOauthType getOauth(String alias) {
			return StringUtil.isEmpty(alias) ? null : Arrays.stream(getValues()).filter(oauth -> oauth.alias.equalsIgnoreCase(alias)).findFirst().orElse(null);
		}

		public static List<ThirdOauthType> queryOf(int flag) {
			return Arrays.stream(getValues()).filter(v -> v.hasBizFlag(flag)).collect(Collectors.toList());
		}

		public static List<ThirdOauthType> queryOf(@NotNull Client client, boolean forOut) {
			Integer flag = convertForClient(client);
			return flag == null ? null : Arrays.stream(getValues()).filter(ThirdOauthType::isEnable).filter(v -> v.hasBizFlag(flag) && (forOut ? v.forOut() : v.forInner())).collect(Collectors.toList());
		}

		private static Integer convertForClient(Client client) {
			Integer flag = null;
			switch (client) {
				case WAP:
					flag = BIZ_WAP;
					break;
				case APP_ANDROID:
					flag = BIZ_ANDROID;
					break;
				case APP_IOS:
					flag = BIZ_IOS;
					break;
			}
			return flag;
		}

		/** 国内 */
		public boolean forInner() {
			return onlyInner() || scope == 2;
		}

		public boolean onlyInner() {
			return Status.NO.eq(scope);
		}

		/** 国外 */
		public boolean forOut() {
			return scope >= 1;
		}

		public boolean onlyOut() {
			return Status.YES.eq(scope);
		}

		public boolean hasBizFlag(final int bizFlag) {
			return BizFlag.hasFlag(this.bizFlag, bizFlag);
		}

	}

}
